package collections

import (
	"context"
	"fmt"
	"gitee.com/baizhige/mongong-go/common"
	"gitee.com/baizhige/mongong-go/db"
	"gitee.com/baizhige/mongong-go/processor"
	"gitee.com/baizhige/mongong-go/query"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
	"math"
)

// Build 得到一个集合对象
func Build[T CollectionName]() *CollOperations[T] {
	var t T
	collName := t.CollectionName()
	operations := &CollOperations[T]{}
	db.PostProcess(func(database *mongo.Database) {
		fmt.Printf("init collection %s\n", collName)
		collection := database.Collection(collName)
		operations.Collection = collection

		db.InCollPoll(db.DefaultDBKey, collName, collection)
	})
	return operations
}

// CollOperations 全称CollectionOperations
type CollOperations[T any] struct {
	*mongo.Collection
}

// InsertOne 插入数据， 插入前提供了插槽，可以注册插入前处理事件
func (t *CollOperations[T]) InsertOne(document interface{},
	opts ...*options.InsertOneOptions) (*mongo.InsertOneResult, error) {
	res := processor.ProcessOne(processor.InsertType, document)
	if res == nil {
		fmt.Println(common.ErrNilDocument.Error())
		return nil, common.ErrNilDocument
	}
	ctx := context.Background()
	return t.Collection.InsertOne(ctx, res, opts...)
}

// InsertMany 插入数据， 插入前提供了插槽，可以注册插入前处理事件
func (t *CollOperations[T]) InsertMany(documents []*T,
	opts ...*options.InsertManyOptions) (*mongo.InsertManyResult, error) {
	res := processor.ProcessMany(processor.InsertType, documents)
	if len(res) == 0 {
		fmt.Println(common.ErrNilDocument.Error())
		return nil, common.ErrNilDocument
	}
	ctx := context.Background()
	return t.Collection.InsertMany(ctx, res, opts...)
}

// ModifyOne 自动补了$set 其中value参数会经过更新插槽处理器
func (t *CollOperations[T]) ModifyOne(filter any, value any,
	opts ...*options.UpdateOptions) (*mongo.UpdateResult, error) {
	res := processor.ProcessOne(processor.UpdateType, value)
	if res == nil {
		fmt.Println(common.ErrNilDocument.Error())
		return nil, common.ErrNilDocument
	}
	ctx := context.Background()
	return t.Collection.UpdateOne(ctx, filter, bson.M{"$set": res}, opts...)
}

// ModifyMany 自动补了$set 其中value参数会经过更新插槽处理器
func (t *CollOperations[T]) ModifyMany(filter any, value any,
	opts ...*options.UpdateOptions) (*mongo.UpdateResult, error) {
	res := processor.ProcessOne(processor.UpdateType, value)
	if res == nil {
		fmt.Println(common.ErrNilDocument.Error())
		return nil, common.ErrNilDocument
	}
	ctx := context.Background()
	return t.Collection.UpdateMany(ctx, filter, bson.M{"$set": res}, opts...)
}

// Find 查询数据并且反序列化， 查询到后 提供了插槽， 可以注册查询后处理事件
func (t *CollOperations[T]) Find(filter any, opts ...*options.FindOptions) (res []*T, err error) {
	ctx := context.Background()
	cursor, err := t.Collection.Find(ctx, filter, opts...)
	if err != nil {
		return
	}
	defer cursor.Close(ctx)
	cursor.All(ctx, &res)
	processor.ProcessMany(processor.QueryType, res)
	return
}

// FindOne 查询数据并且反序列化， 查询到后 提供了插槽， 可以注册查询后处理事件
func (t *CollOperations[T]) FindOne(filter any, opts ...*options.FindOneOptions) (res *T, err error) {
	ctx := context.Background()
	err = t.Collection.FindOne(ctx, filter, opts...).Decode(&res)
	processor.ProcessOne(processor.QueryType, res)
	return
}

// Count 统计数量
func (t *CollOperations[T]) Count(filter any, opts ...*options.CountOptions) (int64, error) {
	ctx := context.Background()
	return t.Collection.CountDocuments(ctx, filter, opts...)
}

// Query 查询全部
func (t *CollOperations[T]) Query(query *query.Query) (res []*T, err error) {
	opt := options.Find()
	if query.Sort != nil {
		opt.SetSort(query.Sort)
	}
	return t.Find(query.Filter, opt)
}

// QueryOne 查询一个
func (t *CollOperations[T]) QueryOne(query *query.Query) (res *T, err error) {
	opt := options.FindOne()
	if query.Sort != nil {
		opt.SetSort(query.Sort)
	}
	return t.FindOne(query.Filter, opt)
}

// QueryPage 分页查询
func (t *CollOperations[T]) QueryPage(q *query.Query) (res *query.PageData[T], err error) {
	if q.Page <= 0 {
		err = common.ErrPageStartIsOne
		return
	}
	if q.Size <= 0 {
		err = common.ErrSizeStartIsOne
		return
	}
	count, err := t.Count(q.Filter)
	if err != nil {
		return
	}
	if count == 0 {
		res = &query.PageData[T]{}
		return
	}
	skip := (q.Page - 1) * q.Size
	if skip >= count {
		return
	}

	// 一顿前置校验都通过 说明可以开始查数据了
	opt := options.Find().SetSkip(skip).SetLimit(q.Size)
	if q.Sort != nil {
		opt.SetSort(q.Sort)
	}
	data, err := t.Find(q.Filter, opt)
	if err != nil {
		return
	}
	totalPage := int64(math.Ceil(float64(count) / float64(q.Size)))
	res = &query.PageData[T]{Page: q.Page, Size: q.Size, Count: count, TotalPages: totalPage, Data: data}
	return
}

// CountByQuery 统计数量
func (t *CollOperations[T]) CountByQuery(q *query.Query) (res int64, err error) {
	res, err = t.Count(q.Filter)
	return
}

// FindById 通过ID查询
func (t *CollOperations[T]) FindById(id any) (res *T, err error) {
	return t.QueryOne(query.Eq("_id", id))
}

// DeleteOne 删除一个文档， 可注册删除后置事件
func (t *CollOperations[T]) DeleteOne(q *query.Query) (res *T, err error) {
	result := t.Collection.FindOneAndDelete(context.Background(), q.Filter)
	err = result.Err()
	if err != nil {
		return
	}
	res = new(T)
	err = result.Decode(res)
	if err != nil {
		return
	}

	processor.ProcessOne(processor.DeleteType, res)
	return
}

// DeleteMany 删除一个文档， 可注册删除后置事件
func (t *CollOperations[T]) DeleteMany(q *query.Query) (res []*T, err error) {
	res, err = t.Query(q)
	if err != nil {
		return
	}
	if len(res) == 0 {
		return
	}
	_, err = t.Collection.DeleteMany(context.Background(), q.Filter)
	if err != nil {
		return
	}
	processor.ProcessMany(processor.DeleteType, res)
	return
}
